using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Resources;
using ObjectLib;
using EngMATLib;
using FftLib;
using BmgNet;
using StaticUtility;

namespace ObjectParser
{
    public class GlidePath : CollectionBase
    {
        public Junction this[int index]
        {
            get
            {
                return ((Junction)List[index]);
            }
            set
            {
                List[index] = value;
            }
        }

        public int Add(Junction vf)
        {
            return (List.Add(vf));
        }

        public int IndexOf(Junction vf)
        {
            return (List.IndexOf(vf));
        }

        public void Insert(int index,Junction vf)
        {
            List.Insert(index,vf);
        }

        public void Remove(Junction vf)
        {
            List.Remove(vf);
        }

        public bool Contains(Junction vf)
        {
            // If value is not of type Junction, this will return false.
            return (List.Contains(vf));
        }

        protected override void OnInsert(int index,object o)
        {
            if(o.GetType().Name != "Junction")
                throw new ArgumentException("value must be of type Junction.","value");
            base.OnInsert(index,o);
        }
        protected override void OnRemove(int index,object o)
        {
            if(o.GetType().Name != "Junction")
                throw new ArgumentException("value must be of type Junction.","value");
            base.OnRemove(index,o);
        }
        protected override void OnSet(int index,object oldValue,object newValue)
        {
            if(newValue.GetType().Name == "Junction")
                throw new ArgumentException("newValue must be of type Junction.","newValue");
            base.OnSet(index,oldValue,newValue);
        }
        protected override void OnValidate(object o)
        {
            if(o.GetType().Name != "Junction")
                throw new ArgumentException("value must be of type Junction.");
            base.OnValidate(o);
        }

        public PathFarray Paths
        {
            get
            {
                PathFarray output = new PathFarray(true);
                for(int i=0;i<this[0].Tail.Polygons.Count;i++)
                    output.Add(this[0].Tail.Polygons[i]);
                if(Head != null)
                {
                    for(int i=0;i<Head.Polygons.Count;i++)
                        if(!output.Contains(Head.Polygons[i]))
                            output.Add(Head.Polygons[i]);
                }
                for(int i=0;i<this.Count;i++)
                    for(int j=0;j<this[i].Intersection.Polygons.Count;j++)
                        if(!output.Contains(this[i].Intersection.Polygons[j]))
                            output.Add(this[i].Intersection.Polygons[j]);
                return output;
            }
        }
        public PathTool Ptool;
        public GlidePath(Junction baseJun,PathTool ptool)
            : base()
        {
            Ptool = ptool;

            this.Add(baseJun);
        }
        public int PointsConsumed(PathF pf)
        {
            int output = 0;
            for(int i=0;i<this.Count;i++)
                if(this[i].Intersection.Polygons.Contains(pf))
                    output++;
            return output;
        }
        public bool Update(float x,float y)
        {
            if(this.Count > 0)
            {
                bool updated = this[this.Count-1].Update(x,y);
                if(updated)
                {
                    if(this[this.Count-1].JunctionEvent==Junction.JunctionEvents.TailEvent)
                    {
                        //	Only remove this junction if there are other junctions left.
                        if(this.Count > 1)
                        {
                            Junction jun = this[this.Count-1];

                            //	Invalidate the previous head of the GlidePath.
                            Ptool.Owner.Pb.Invalidate(Ptool.Owner.InvPbRect(VertexF.GetBoundingRect(
                                new PointF(Ptool.Owner.ImgToPbX(jun.Intersection.X),Ptool.Owner.ImgToPbY(jun.Intersection.Y)),PathTool.RADIUS+2)));

                            PointF pt1 = new PointF(Ptool.Owner.ImgToPbX(jun.Intersection.X),Ptool.Owner.ImgToPbY(jun.Intersection.Y));
                            PointF pt2 = new PointF(Ptool.Owner.ImgToPbX(jun.Tail.X),Ptool.Owner.ImgToPbY(jun.Tail.Y));

                            //	Invalidate the old leg.
                            Ptool.Owner.Pb.Invalidate(Ptool.Owner.InvPbRect(
                                VertexF.GetBoundingRect(pt1,pt2,PathTool.RADIUS+1)));

                            ArrayList p = jun.Polygons;
                            this.RemoveAt(this.Count-1);
                            UpdateDrawCache();

                            //	Return that an update occurred.
                            return true;
                        }
                    }
                    else if(this[this.Count-1].JunctionEvent==Junction.JunctionEvents.TargetEvent)
                    {
                        //	Only add this junction if it is not already part of this GlidePath and if it does not
                        //	result in the GlidePath entirely consuming an entire polygon.
                        Junction nextJun = this[this.Count-1].NextJunction;
                        bool proceed = true;
                        for(int i=0;i<this.Count;i++)		//	Check to see if not already part of the glide path
                            if(nextJun.Intersection == this[i].Intersection)
                            {
                                proceed = false;
                                break;
                            }
                        if(proceed)							//	Check to see if any of the new objects polygons will be entirely consumed.
                        {
                            ArrayList newPaths = new ArrayList();
                            for(int i=0;i<nextJun.Intersection.Polygons.Count;i++)
                            {
                                int pc = PointsConsumed((PathF)nextJun.Intersection.Polygons[i]);
                                if(((PathF)nextJun.Intersection.Polygons[i]).Count <= pc)
                                {
                                    proceed = false;
                                    break;
                                }
                            }
                        }
                        if(proceed)
                        {
                            //	Check to see if the new junction's intersection contains the active polygon in its list of Polygons.
                            if(nextJun.Intersection.Polygons.Contains(Ptool.Owner.Opd.ActivePoly))
                                proceed = false;
                        }
                        if(proceed)
                        {
                            this.Add(nextJun);
                            UpdateDrawCache();

                            //	A point has been added to the glide path.  Invalidate
                            PathFarray p = Paths;
                            for(int i=0;i<p.Count;i++)
                                Ptool.Owner.Pb.Invalidate(Ptool.Owner.InvPbRect(Ptool.Owner.ImgToPbRect(p[i].GetBoundingRect(2))));

                            //	Invalidate the previous head of the GlidePath.
                            Ptool.Owner.Pb.Invalidate(Ptool.Owner.InvPbRect(VertexF.GetBoundingRect(
                                new PointF(Ptool.Owner.ImgToPbX(nextJun.Tail.X),Ptool.Owner.ImgToPbY(nextJun.Tail.Y)),PathTool.RADIUS+2)));

                            //	Invalidate the head of the GlidePath.
                            Ptool.Owner.Pb.Invalidate(Ptool.Owner.InvPbRect(VertexF.GetBoundingRect(
                                new PointF(Ptool.Owner.ImgToPbX(nextJun.Intersection.X),Ptool.Owner.ImgToPbY(nextJun.Intersection.Y)),PathTool.RADIUS+2)));

                            return true;
                        }
                    }
                }
            }
            return false;
        }
        public PointF[] DrawCache;
        public void UpdateDrawCache()
        {
            //	Initialize the draw cache if necessary.
            if(DrawCache == null)
                DrawCache = new PointF[this.Count];
            if(DrawCache.Length != this.Count)
                DrawCache = new PointF[this.Count];

            //	Update the draw cache.
            for(int i=0;i<this.Count;i++)
            {
                DrawCache[i] = new PointF(
                    Ptool.Owner.ImgToPbX(this[i].Intersection.X),
                    Ptool.Owner.ImgToPbY(this[i].Intersection.Y));

            }
        }
        public void Draw(Graphics g,Pen pen)
        {
            if(DrawCache != null)
            {
                for(int i=0;i<DrawCache.Length-1;i++)
                {
                    g.DrawLine(pen,DrawCache[i],DrawCache[i+1]);
                }
                g.DrawEllipse(pen,VertexF.GetPtDrawRect(DrawCache[DrawCache.Length-1],5));
            }
        }
        public VertexF Head;
        /// <summary>
        /// Given the active contour con, find the appropriate base2 attachment point and then attach this GlidePath there.
        /// The attachment process updates the contour's list of VertexF objects and each VertexF list of Contour objects.
        /// Also updates the member variable Head = head.
        /// </summary>
        /// <param name="vf1"></param>
        /// <param name="pf"></param>
        /// <returns>VertexF  The base2 attachment point.  If no attachment was possible, this variable is null.</returns>
        public void Attach(ContourF con,VertexF head)
        {
            Head = head;
            //	Now we need to knonw if the head is "before" the tail because we want to insert the new points
            //	between the head and the tail.
            if(Head==this[0].Tail)
            {
                if(Head==con[0])
                {
                    //	First, from the side of the contour.
                    con.Insert(0,this[0].Intersection);
                    //	Second, from the side of VertexF.
                    if(!this[0].Intersection.Contours.Contains(con))
                        this[0].Intersection.Contours.Add(con);
                }
                else if(Head==con[con.Count-1])
                {
                    //	First, from the side of the contour.
                    con.Add(this[0].Intersection);
                    //	Second, from the side of VertexF.
                    if(!this[0].Intersection.Contours.Contains(con))
                        this[0].Intersection.Contours.Add(con);
                }
            }
            else
            {
                int hInd = con.IndexOf(Head);
                int tInd = con.IndexOf(this[0].Tail);
                int hprev = hInd-1;
                int ii = hInd;
                if(hprev==tInd)	//	The tInd is earlier than the Head node.  Insert after tInd from 0 to this.Count-1.
                {
                    ii = tInd;

                    //	Now we know where to begin insertions(immediately after ii).  Increment ii prior to each insertion.
                    for(int i=0;i<this.Count;i++)
                    {
                        //	First, from the side of the contour.
                        con.Insert(++ii,this[i].Intersection);
                        //	Second, from the side of VertexF.
                        if(!this[i].Intersection.Contours.Contains(con))
                            this[i].Intersection.Contours.Add(con);
                    }
                }
                else	//	the Head is earlier in the tail.  Add backwards, from this.Count-1 to 0.
                {
                    //	Now we know where to begin insertions(immediately after ii).  Increment ii prior to each insertion.
                    for(int i=this.Count-1;i>=0;i--)
                    {
                        //	First, from the side of the polygon.
                        con.Insert(++ii,this[i].Intersection);
                        //	Second, from the side of VertexF.
                        if(!this[i].Intersection.Contours.Contains(con))
                            this[i].Intersection.Contours.Add(con);
                    }
                }
            }
        }
        /// <summary>
        /// Given the active polygon pf, find the appropriate base2 attachment point and then attach this GlidePath there.
        /// The attachment process updates the polygon's list of VertexF objects and each VertexF list of PolygonF objects.
        /// Also updates the member variable Head = head.
        /// </summary>
        /// <param name="vf1"></param>
        /// <param name="pf"></param>
        /// <returns>VertexF  The base2 attachment point.  If no attachment was possible, this variable is null.</returns>
        public void Attach(PolygonF pf,VertexF head,Bitmap bmp,Color paintColor)
        {
            Head = head;
            //	Now we need to knonw if the head is "before" the table because we want to insert the new points
            //	between the head and the tail.
            int hInd = pf.IndexOf(Head);
            int tInd = pf.IndexOf(this[0].Tail);
            int hprev = hInd-1;
            if(hprev < 0)
                hprev = pf.Count-1;
            int ii = hInd;
            if(hprev==tInd)	//	The tInd is earlier than the Head node.  Insert after tInd from 0 to this.Count-1.
            {
                ii = tInd;

                //	Now we know where to begin insertions(immediately after ii).  Increment ii prior to each insertion.
                for(int i=0;i<this.Count;i++)
                {
                    //	First, from the side of the polygon.
                    pf.Insert(++ii,this[i].Intersection);
                    //	Second, from the side of VertexF.
                    if(!this[i].Intersection.Polygons.Contains(pf))
                        this[i].Intersection.Polygons.Add(pf);
                }
            }
            else	//	the Head is earlier in the tai.  Add backwards, from this.Count-1 to 0.
            {
                //	Now we know where to begin insertions(immediately after ii).  Increment ii prior to each insertion.
                for(int i=this.Count-1;i>=0;i--)
                {
                    //	First, from the side of the polygon.
                    pf.Insert(++ii,this[i].Intersection);
                    //	Second, from the side of VertexF.
                    if(!this[i].Intersection.Polygons.Contains(pf))
                        this[i].Intersection.Polygons.Add(pf);
                }
            }
            AfterAttach(pf,bmp,paintColor);
        }
        public void AfterAttach(PolygonF pf,Bitmap bmp,Color paintColor)
        {
            PixelTagger pix = pf.Pix;

            PolygonF changed = GetChangedRegion();
            Rectangle changedBound = changed.GetBoundingRect(0.0f);

            PolygonFlist sect = new PolygonFlist();		//	The list of overlapping polygons.
            for(int i=0;i<pf.Swallowed.Count;i++)
                if(PolygonF.Overlaps(changed,pf.Swallowed[i]))
                    sect.Add(pf.Swallowed[i]);
            if(bmp==null)
            {
                for(int x = changedBound.Left;x <= changedBound.Right;x++)
                    for(int y = changedBound.Top;y<= changedBound.Bottom;y++)
                        if(changed.InPoly(x,y)>0 && !sect.ContainsPoint(x,y))
                            pix.AddPixel(x,y);
            }
            else
            {
                int left = Math.Max(changedBound.Left,0);
                int right = Math.Min(changedBound.Right,bmp.Width+1);
                int top = Math.Max(changedBound.Top,0);
                int bottom = Math.Min(changedBound.Bottom,bmp.Height+1);
                for(int x = left;x <= right;x++)
                    for(int y = top;y<= bottom;y++)
                        if(changed.InPoly(x,y)>0 && !sect.ContainsPoint(x,y))
                        {
                            bmp.SetPixel(x,y,paintColor);
                            pix.AddPixel(x,y);
                        }
            }
        }
        public void AfterDetatch(PolygonF pf,Bitmap bmp)
        {
            PixelTagger pix = pf.Pix;

            PolygonF changed = GetChangedRegion();
            Rectangle changedBound = changed.GetBoundingRect(0.0f);
            if(bmp==null)
            {
                for(int x = changedBound.Left;x <= changedBound.Right;x++)
                    for(int y = changedBound.Top;y<= changedBound.Bottom;y++)
                        pix.RemovePixel(x,y);
            }
            else
            {
                int left = Math.Max(changedBound.Left,0);
                int right = Math.Min(changedBound.Right,bmp.Width+1);
                int top = Math.Max(changedBound.Top,0);
                int bottom = Math.Min(changedBound.Bottom,bmp.Height+1);
                for(int x = left;x <= right;x++)
                    for(int y = top;y<= bottom;y++)
                        if(changed.InPoly(x,y)>0)
                        {
                            bmp.SetPixel(x,y,Color.Transparent);
                            pix.RemovePixel(x,y);
                        }
            }
        }
        /// <summary>
        /// Removes the polygon from the list of Polygons for all VertexF instances in this.
        /// </summary>
        /// <param name="pf"></param>
        public void RemovePolyFromVertexF(PolygonF pf)
        {
            for(int i=0;i<this.Count;i++)
                this[i].Intersection.Polygons.Remove(pf);
        }
        public Rectangle GetBoundingRect()
        {
            float left = Math.Min(this[0].Tail.X,Head.X);
            float right = Math.Max(this[0].Tail.X,Head.X);
            float top = Math.Min(this[0].Tail.Y,Head.Y);
            float bottom = Math.Max(this[0].Tail.Y,Head.Y);
            for(int i=0;i<this.Count;i++)
            {
                if(this[i].Intersection.X < left) left = this[i].Intersection.X;
                if(this[i].Intersection.X > right) right = this[i].Intersection.X;
                if(this[i].Intersection.Y < top) top = this[i].Intersection.Y;
                if(this[i].Intersection.Y > bottom) bottom = this[i].Intersection.Y;
            }
            return new Rectangle((int)left,(int)top,(int)(right-left)+2,(int)(bottom-top)+2);
        }
        public PolygonF GetChangedRegion()
        {
            PolygonF output = new PolygonF(null);
            if(this.Count >= 1)
            {
                output.Add(this[0].Tail);
                for(int i=0;i<this.Count;i++)
                    output.Add(this[i].Intersection);
                output.Add(this.Head);
            }
            return output;
        }
    }
    /// <summary>
    /// Supports edge tracking during link mode.
    /// </summary>
    public class Junction
    {
        public ArrayList Targets;
        public VertexF Intersection;
        public VertexF Tail;
        public JunctionEvents JunctionEvent;
        public double xStylus,yStylus;
        public VertexF UpdatedTarget;
        public VertexF ClosestTarget
        {
            get
            {
                VertexF output = null;
                if(Targets.Count > 0)
                {
                    output = (VertexF)Targets[0];
                    if(Targets.Count > 1)
                    {
                        double x = (double)output.X;
                        double y = (double)output.Y;
                        double dx = x-xStylus;
                        double dy = y-yStylus;
                        double dist = Math.Sqrt(dx*dx + dy*dy);
                        double minDist = dist;
                        VertexF vf;
                        for(int i=1;i<Targets.Count;i++)
                        {
                            vf = (VertexF)Targets[i];
                            dist = DistanceFromStylus(vf);
                            if(dist < minDist)
                            {
                                minDist = dist;
                                output = vf;
                            }
                        }
                    }
                }
                return output;
            }
        }
        public Junction NextJunction
        {
            get
            {
                if(JunctionEvent==JunctionEvents.TargetEvent)
                {
                    //	intersection (UpdateTarget), tail (Intersection).
                    return new Junction(UpdatedTarget,Intersection);
                }
                else if(JunctionEvent==JunctionEvents.TailEvent)
                {
                    //	intersection (Tail), tail (Intersection).
                    return new Junction(Tail,Intersection);
                }
                return null;
            }
        }
        public ArrayList Polygons
        {
            get
            {
                ArrayList output = new ArrayList();
                for(int i=0;i<Intersection.Polygons.Count;i++)
                {
                    //	Any polygons that the Intersection and Tail have in common.
                    if(Tail.Polygons.Contains(Intersection.Polygons[i]))
                        output.Add(Intersection.Polygons[i]);
                    else
                    {
                        //	Any polygons that the intersection and at least one targets has in common.
                        for(int j=0;j<Targets.Count;j++)
                        {
                            if(((VertexF)Targets[j]).Polygons.Contains(Intersection.Polygons[i]))
                            {
                                output.Add(Intersection.Polygons[i]);
                                break;
                            }
                        }
                    }
                }
                return output;
            }
        }
        public double DistanceFromStylus(VertexF vf)
        {
            double x = (double)vf.X;
            double y = (double)vf.Y;
            double dx = x-xStylus;
            double dy = y-yStylus;
            return Math.Sqrt(dx*dx + dy*dy);
        }
        public Junction(VertexF intersection,VertexF tail)
        {
            Intersection = intersection;
            Tail = tail;
            JunctionEvent = JunctionEvents.NoEvent;
            xStylus = (double)Intersection.X;
            yStylus = (double)Intersection.Y;
            UpdatedTarget = null;

            Targets = new ArrayList();
            for(int i=0;i<Intersection.Polygons.Count;i++)
            {
                //	add the adjacent vertices (besides the tail).
                //	don't add if the VertexF is already in the list of Targets.
                int ind = Intersection.Polygons[i].IndexOf(Intersection);

                int prev = ind-1;
                if(prev < 0) prev = Intersection.Polygons[i].Count-1;
                VertexF vf = Intersection.Polygons[i][prev];
                if(vf != Tail && !Targets.Contains(vf))
                    Targets.Add(vf);

                int next = ind + 1;
                if(next > Intersection.Polygons[i].Count-1)
                    next = 0;
                vf = Intersection.Polygons[i][next];
                if(vf != Tail && !Targets.Contains(vf))
                    Targets.Add(vf);
            }
        }
        public Junction(ArrayList targets,VertexF intersection,VertexF tail)
        {
            Targets = targets;
            Intersection = intersection;
            Tail = tail;
            JunctionEvent = JunctionEvents.NoEvent;
            xStylus = (double)Intersection.X;
            yStylus = (double)Intersection.Y;
            UpdatedTarget = null;
        }
        /// <summary>
        /// Given the stylus coordinates in Img space,  returns true if a significant event has occurred.  A significant event is when
        /// the stylus moves beyond the tail or a target.
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns>True if a significant event has occurred.  JunctionEvent tells which type of event.  If it was a TargetEvent,
        /// then UpdatedTarget holds the relevant target.</returns>
        public bool Update(float x,float y)
        {
            UpdatedTarget = null;
            xStylus = (double)x;
            yStylus = (double)y;

            //	More than two points.  Draw the closest point and its mate, which is the point attached to the closest segment.
            double[] theta = new double[Targets.Count+1];
            for(int i=0;i<Targets.Count;i++)
                theta[i] = VertexF.Angle(Intersection,(VertexF)Targets[i]);
            theta[Targets.Count] = VertexF.Angle(Intersection,Tail);

            PointF stylus = new PointF(x,y);
            double pi2 = Math.PI*2.0;
            double stylusTheta = VertexF.Angle(Intersection,stylus);

            //	Compute distance between theta and stylusTheta.
            for(int i=0;i<theta.Length;i++)
            {
                theta[i] = (theta[i]-stylusTheta)%pi2;
                if(theta[i] < 0)
                    theta[i] += pi2;
                if(theta[i] > Math.PI)
                    theta[i] = pi2-theta[i];
            }
            //	Find the minimum distance to stylusTheta
            int iMin = 0;
            double thetaMin = theta[0];
            for(int i=1;i<theta.Length;i++)
            {
                if(theta[i] < thetaMin)
                {
                    thetaMin = theta[i];
                    iMin = i;
                }
            }
            //	Grab the VertexF associated with that minimum
            VertexF cand = Tail;
            if(iMin < Targets.Count)
                cand = (VertexF)Targets[iMin];

            //	If the distance of the candidate is greater than the distance of the stylus, then signal an update.
            double distCand = VertexF.Distance(cand,Intersection);
            double distStyl = VertexF.Distance(stylus,Intersection);
            if(distStyl > distCand)
            {
                if(cand==Tail)
                {
                    JunctionEvent = JunctionEvents.TailEvent;
                    return true;
                }
                else
                {
                    JunctionEvent = JunctionEvents.TargetEvent;
                    UpdatedTarget = cand;
                    return true;
                }
            }
            return false;
        }
        /// <summary>
        /// List of possible events that can happen for a junction.
        /// </summary>
        public enum JunctionEvents { NoEvent, TargetEvent, TailEvent };
    }
    /// <summary>
    /// A class for drawing arrows onto the picture box control.
    /// </summary>
    public class Arrow
    {
        public PointF Center;
        public double Radians;
        public double Length;
        public double Width;
        public SolidBrush FillBrush;
        public PointF[] Points;
        public Rectangle BoundingRect;
        private static float xmin,xmax,ymin,ymax;
        private static double rot,sin,cos,x,y;
        private static PointF pt;
        public Arrow(PointF center,double radians) : this(center,radians,10.0,5.0,Color.Red) { }
        public Arrow(PointF center,double radians,double length,double width,Color fillColor)
        {
            Center =  center;
            Length = length;
            Width = width;
            FillBrush = new SolidBrush(fillColor);
            Radians = radians;

            //	Initialize the points facing at 90 degrees or pi/2 radians.  length and width are both 1 centered on 0.
            Points = new PointF[7];
            /*
            Points[0] = new PointF(0.0f, 0.5f);	//	Arrowhead facing up
            Points[1] = new PointF(-0.5f, 0.25f);
            Points[2] = new PointF(-0.15f, 0.25f);
            Points[3] = new PointF(-0.15f, -0.5f);
            Points[4] = new PointF(0.15f, -0.5f);
            Points[5] = new PointF(0.15f, 0.25f);
            Points[6] = new PointF(0.5f, 0.25f);
            */
            Points[0] = new PointF(0.0f,0.0f);	//	Arrowhead facing up
            Points[1] = new PointF(-0.5f,-0.25f);
            Points[2] = new PointF(-0.15f,-0.25f);
            Points[3] = new PointF(-0.15f,-1.0f);
            Points[4] = new PointF(0.15f,-1.0f);
            Points[5] = new PointF(0.15f,-0.25f);
            Points[6] = new PointF(0.5f,-0.25f);


            //	Transform.
            rot = Radians-Math.PI/2;		//we're already pointing at pi/2, so subtract it.
            sin = Math.Sin(rot);
            cos = Math.Cos(rot);
            for(int i=0;i<Points.Length;i++)
            {
                //	Scale
                Points[i].X *= (float)Width;
                Points[i].Y *= (float)Length;

                //	Rotate
                x = (double)Points[i].X*cos - (double)Points[i].Y*sin;		//	negative Y coordinate due to screen flip
                y = -(double)Points[i].X*sin - (double)Points[i].Y*cos;		//	negative Y coordinate due to screen flip
                Points[i] = pt = new PointF((float)x+Center.X,(float)y+Center.Y);		//	This also translates the points to the center.
                if(i==0)
                {
                    xmin=xmax=pt.X;
                    ymin=ymax=pt.Y;
                }
                else
                {
                    if(pt.X<xmin)
                        xmin=pt.X;
                    else if(pt.X>xmax)
                        xmax=pt.X;
                    if(pt.Y<ymin)
                        ymin=pt.Y;
                    else if(pt.Y>ymax)
                        ymax=pt.Y;
                }
            }
            BoundingRect = Rectangle.FromLTRB((int)xmin,(int)ymin,(int)(xmax+1),(int)(ymax+1));
        }
        public void Paint(object sender,PaintEventArgs e)
        {
            e.Graphics.FillPolygon(FillBrush,Points);
        }
    }
}